﻿using System;
using System.Collections.Generic;

namespace proba_pera
{
    public class Ways:RwpBaseObject
    {

        private readonly List<Waypoint> _waypoints;
        private readonly Collector _collector;
        private RwpUnit _neighUnit;
        //коэф. для перебора соседних точек
        private readonly Waypoint[] _neighbors =
            {
                new Waypoint(1, 0),
                new Waypoint(1, -1),
                new Waypoint(0, -1),
                new Waypoint(-1, -1),
                new Waypoint(-1, 0),
                new Waypoint(-1, 1),
                new Waypoint(0, 1),
                new Waypoint(1, 1)
            };
        public Ways(Collector collector)
        {
            _collector = collector;
            _waypoints = new List<Waypoint>();

            
            /*
            _neighbors[1].X = 1;
            _neighbors[1].Y = 0;
            _neighbors[2].X = 1;
            _neighbors[2].Y = -1;
            _neighbors[3].X = 0;
            _neighbors[3].Y = -1;
            _neighbors[4].X = -1;
            _neighbors[4].Y = -1;
            _neighbors[5].X = -1;
            _neighbors[5].Y = 0;
            _neighbors[6].X = -1;
            _neighbors[6].Y = 1;
            _neighbors[7].X = 0;
            _neighbors[7].Y = 1;
            _neighbors[8].X = 1;
            _neighbors[8].Y = 1;*/
        }

        //есть в опене
// ReSharper disable InconsistentNaming
        private int inList(IEnumerable<Waypoint> list, Waypoint _waypoint)
// ReSharper restore InconsistentNaming
        {
            if (list == null) throw new ArgumentNullException("list");
            if (_waypoint == null) throw new ArgumentNullException("_waypoint");
            int i = 0;
            foreach (Waypoint waypoint in list)
            {
                if (waypoint.X == _waypoint.X && waypoint.Y == _waypoint.Y)
                {
                    return i;
                }
                i++;
            }
            return -1;
        }

        //рекурсивно получить список в _вейпоинтс
        private void GetWaypoints(Waypoint waypoint)
        {
            if (waypoint == null) return;
            _waypoints.Add(waypoint);
            waypoint = waypoint.Parent;
            GetWaypoints(waypoint);
        }

        private Waypoint GetBetter(List<Waypoint> list)
        {
            Waypoint buf = null;
            foreach (var waypoint in list)
            {
                if (buf == null) buf = waypoint;
                if (buf.FromStartCost + buf.ToEndCost > waypoint.ToEndCost + waypoint.FromStartCost)
                    buf = waypoint;
            }
            return buf;
        }

        //A*        
        private Waypoint Astar(int startX, int startY, int endX, int endY)
        {
            //int iterations = new int();
            //iterations = 0;
            List<Waypoint> open = new List<Waypoint>();
            var closed = new List<Waypoint>();
            var cur= new Waypoint(startX, startY);
            cur.ToEndCost = 10 * Convert.ToInt32(Math.Sqrt((startX - endX) * (startX - endX) + (startY - endY) * (startY - endY)));
            cur.Parent = null;
            open.Add(cur);
            ToLog("WAYPOINTS: ");
            while (open.Count > 0)
            {
                //iterations++;
                cur = GetBetter(open);
                string str = "Current waypoint: " + cur.X + "/" + cur.Y;
                ToLog(str);
                if (cur.X == endX && cur.Y == endY)
                {
                    str = "Найдена конечная точка: " + cur.X + "/" + cur.Y;
                    ToLog(str);
                    return cur;
                }
                foreach (var neighbor in _neighbors)
                {
                    int neighX = cur.X + neighbor.X;
                    int neighY = cur.Y + neighbor.Y;
// ReSharper disable SpecifyACultureInStringConversionExplicitly
                    str = "Neighbor xy:" + neighX.ToString() + ':' + neighY.ToString();
// ReSharper restore SpecifyACultureInStringConversionExplicitly
                    ToLog(str);
                    _neighUnit = _collector.SearchByXy(neighX, neighY);
                    Waypoint buf = new Waypoint(neighX, neighY);
                    if (_neighUnit == null && inList(closed, buf) == -1)
                    {
                        str = "---free neighbor waypoint: " + buf.X + "/" + buf.Y;
                        ToLog(str);
                        int dx = (buf.X - endX)*(buf.X - endX);
                        int dy = (buf.Y - endX)*(buf.Y - endX);
                        buf.FromStartCost = cur.FromStartCost;
                        buf.FromStartCost += neighbor.X == neighbor.Y ? 14 : 10;
                        buf.ToEndCost = Convert.ToInt32(Math.Sqrt(dx + dy))*10;
                        buf.Parent = cur;
                        int position = inList(open, buf);
                        if (position > -1)
                        {
                            if (open[position].FromStartCost + open[position].ToEndCost >
                                buf.FromStartCost + buf.ToEndCost)
                            {
                                str = "---buffered neighbor is better of same waypoint in open list";
                                ToLog(str);
                                open[position].FromStartCost = buf.FromStartCost;
                                open[position].Parent = buf.Parent;
                            }
                        }
                        else
                        {
                            str = "---add neighbor to open list: " + buf.X + "/" + buf.Y;
                            ToLog(str);
                            open.Add(buf);
                        }
                    }
                }
                closed.Add(cur);
                open.Remove(cur);
            }
            return null;
        }
        
        //возвращение списка вейпойнтов
        public List<Waypoint> Getlist(int startX, int startY, int endX, int endY)
        {
            _waypoints.Clear();
            Waypoint a = Astar(startX, startY, endX, endY);
            GetWaypoints(a);
            return _waypoints;
        }

    }
}